import { cloneDeep, defaultsDeep } from 'lodash';
import { useMemo, useRef, useState } from 'react';
import {
  CommandDialogProps,
  NonDialogCommandForm,
  NonDialogCommandFormProps,
} from '.';
import { NONE_TITLE_UISCHEMA, selectionIDPrefix } from './constants';
import { PickableFormContext } from './pickable-provider';
import { CustomBaseInputTemplate } from './templates';
import { CustomSelectWidget } from './widgets/multi-select';
import { RJSFSchema } from '@rjsf/utils';
import { CommandDialog } from '@amcax/base';

const recoverPickData = (obj: any, selectionMap: Map<string, any>) => {
  for (const property in obj) {
    if (
      Array.isArray(obj) ||
      Object.prototype.hasOwnProperty.call(obj, property)
    ) {
      const value = obj[property];
      if (typeof value === 'object') {
        recoverPickData(value, selectionMap);
      } else if (
        typeof value === 'string' &&
        value.startsWith(selectionIDPrefix)
      ) {
        obj[property] = selectionMap.get(value);
      }
    }
  }

  return obj;
};

const parseFormData = (obj: any, selectionMap: Map<string, any>) => {
  const tryAdd = (uuid: string, target: any) => {
    if (!selectionMap.has(`${selectionIDPrefix}${uuid}`)) {
      selectionMap.set(`${selectionIDPrefix}${uuid}`, target);
    }
  };

  const parseObj = (o: any) => {
    const result = Array.isArray(o) ? [] : {};

    for (const property in o) {
      if (Object.prototype.hasOwnProperty.call(obj, property)) {
        const value = obj[property];
        if (value?.assisObj?.name === 'cspoint') {
          tryAdd(value.assisObj.uuid, value);
          result[property] = `${selectionIDPrefix}${value.assisObj.uuid}`;
        } else if (
          value?.assisObj?.getType?.() === 'PickLine' ||
          value?.assisObj?.getType?.() === 'PickCurve'
        ) {
          tryAdd(value.assisObj.uuid, value);
          result[property] = `${selectionIDPrefix}${value.assisObj.uuid}`;
        } else if (
          value?.assisObj?.getType?.() === 'PickFace' &&
          value.assisObj.mesh
        ) {
          tryAdd(value.assisObj.mesh.uuid, value);
          result[property] = `${selectionIDPrefix}${value.assisObj.mesh.uuid}`;
        } else if (
          value?.assisObj?.getType?.() === 'PickObject' &&
          value.assisObj.modelDxid
        ) {
          tryAdd(value.assisObj.modelDxid, value);
          result[property] = `${selectionIDPrefix}${value.assisObj.modelDxid}`;
        } else if (value?.idInfo?.o != null) {
          tryAdd(value?.idInfo?.o, value);
          result[property] = `${selectionIDPrefix}${value?.idInfo?.o}`;
        } else if (typeof value === 'object') {
          result[property] = parseFormData(value, selectionMap);
        } else {
          result[property] = value;
        }
      }
    }
    return result;
  };

  if (typeof obj === 'object' && obj != null) {
    return parseObj(obj);
  } else {
    return obj;
  }
};

type NonDialogPickableCommandFormProps = Omit<
  NonDialogCommandFormProps,
  'onChange' | 'onSubmit'
> & {
  onChange?: (formData: any, rawData: any) => void;
  onSubmit?: (formData: any, rawData: any) => void;
};

export const NonDialogPickableCommandForm = ({
  onSubmit,
  onChange,
  formData,
  extraUiSchema,
  ...rest
}: NonDialogPickableCommandFormProps) => {
  const { schema } = rest;

  // 对单层的 schema 作简单处理：
  // 如果没有title自动加上ui:options.label = false
  const uiSchema = useMemo(() => {
    if (schema.properties == null) {
      return extraUiSchema;
    }
    const getSimpleCustomUiSchema = (innerSchema: RJSFSchema) => {
      const result = {};
      Object.keys(innerSchema.properties).forEach((key) => {
        const value = innerSchema.properties[key] as any;
        if (value.type === 'object' && value.properties != null) {
          result[key] = getSimpleCustomUiSchema(value);
        } else {
          if (!value?.title) {
            result[key] = NONE_TITLE_UISCHEMA;
          }
        }
      });

      return result;
    };

    const simpleCustomUiSchemaFromSchema = getSimpleCustomUiSchema(schema);

    return defaultsDeep({ ...extraUiSchema }, simpleCustomUiSchemaFromSchema);
  }, [schema, extraUiSchema]);

  const selectionMap = useRef(new Map<string, any>());
  const [innerFormData, setInnerFormData] = useState(formData);

  // 传入的formData也需要加入selectionMap
  const controlledFormData = useMemo(() => {
    if (formData == null) {
      return null;
    }

    return parseFormData(formData, selectionMap.current);
  }, [formData]);

  return (
    <PickableFormContext.Provider value={selectionMap.current}>
      <NonDialogCommandForm
        {...rest}
        extraUiSchema={uiSchema}
        formData={formData == null ? innerFormData : controlledFormData}
        onChange={(e) => {
          if (formData == null) {
            setInnerFormData(e.formData);
          }
          onChange?.(
            recoverPickData(cloneDeep(e.formData), selectionMap.current),
            e.formData,
          );
        }}
        onSubmit={(formData) => {
          onSubmit?.(
            recoverPickData(cloneDeep(formData), selectionMap.current),
            formData,
          );
        }}
        templates={{
          BaseInputTemplate: CustomBaseInputTemplate,
          ...rest.templates,
        }}
        widgets={{
          SelectWidget: CustomSelectWidget,
          ...rest.widgets,
        }}
      />
    </PickableFormContext.Provider>
  );
};

type PickableCommandFormProps = NonDialogPickableCommandFormProps &
  CommandDialogProps;

export const PickableCommandForm = ({
  title,
  onClose,
  placement,
  actions, //action可作为CommandDialog的footer组件,若改变NonDialogPickableCommandForm表单footer-直接写入children 即可
  ...rest
}: PickableCommandFormProps) => {
  return (
    <CommandDialog
      title={title}
      onClose={onClose}
      actions={actions}
      placement={placement ?? 'topLeft'}
    >
      <NonDialogPickableCommandForm
        {...rest}
        onCancel={onClose}
      />
    </CommandDialog>
  );
};
